package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl implements DeviceService {
    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;
    @Autowired
    private Client client;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 在阿里云的IOT平台同步数据信息
     */
    @Override
    public void syncProductList() {
        // 需要什么参数就设置什么参数
        QueryProductListRequest request = new QueryProductListRequest();
        // 设置请求的参数 一页展示100行产品的记录数据
        request.setPageSize(100);
        request.setCurrentPage(1);
        // 设置实例的ID
        request.iotInstanceId = aliIoTConfigProperties.getIotInstanceId();

        // 获取返回的结果数据
        QueryProductListResponse response;
        try {
            response = client.queryProductList(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (!response.getBody().success) {
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }
        // 解析获取到的产品数据信息
        List<ProductVo> productVoList = response.getBody().getData().getList().productInfo.stream()
                .map(p -> BeanUtil.toBean(p, ProductVo.class))
                .collect(Collectors.toList());
        if (productVoList != null && productVoList.size() > 0) {
            // 把数据存储到本地的内存当中，redis数据库里面
            redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSON.toJSONString(productVoList));
        }
    }

    /**
     * 查询所有产品列表
     *
     * @return 产品列表
     */
    @Override
    public List<ProductVo> allProduct() {

        //从缓存中获取数据
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        //如果为空，返回空集合
        if (StringUtils.isEmpty(jsonStr)) {
            return Collections.emptyList();
        }
        //转换数据，并返回
        return JSONUtil.toList(jsonStr, ProductVo.class);
    }

    /**
     * 新增一个设备信息
     *
     * @param deviceDto
     * @return
     */
    @Override
    public ResponseResult<Void> registerDevice(DeviceDto deviceDto) {
        // 第一步 首先判断设备的名字是否重复
        int count = deviceMapper.countSelectByDeviceName(deviceDto.getDeviceName());
        if (count > 0) {
            // 设备名字已经存在、报一个异常、请重新输入信息
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }
        // 第二步 调用IOT 阿里云的API接口 新增一个设备  请求的三个参数 前端已经封装好了
        RegisterDeviceRequest registerDeviceRequest = deviceDto.getRegisterDeviceRequest();
        registerDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse registerDeviceResponse = null;
        try {
            registerDeviceResponse = client.registerDevice(registerDeviceRequest);
        } catch (Exception e) {
            // IOT 注册接口调用失败
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        // 先判断接口是否接通
        if (registerDeviceResponse == null || !registerDeviceResponse.getBody().success) {
            // 把出错的信息的全部打印出来
            throw new RuntimeException(registerDeviceResponse.getBody().getErrorMessage());
        }
        // 第三步 到这一步 IOT 阿里云平台新增成功  现在往数据库里面的表里面插入数据（准备请求的参数）
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        // 往设备里面添加设备的ID 在IOT阿里云平台里面添加一个设备、就会自动生成一个唯一的设备ID
        device.setIotId(registerDeviceResponse.getBody().getData().getIotId());
        // 还要设置一个属性值 调用IOT阿里云平台的第三方API接口 查询设备的名字  又调用了一次的API接口
        QueryProductRequest queryProductRequest = new QueryProductRequest();
        queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        queryProductRequest.setProductKey(deviceDto.getProductKey());
        QueryProductResponse queryProductResponse;
        try {
            queryProductResponse = client.queryProduct(queryProductRequest);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (queryProductResponse.getBody().success) {
            // 判断一下接口是否通了 ，如果接口通了 就设置产品的名字
            device.setProductName(queryProductResponse.getBody().getData().getProductName());
        }
           // 判断设备是 0 随身的设备 1 代表的是固定的设备
        if(device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            // 如果是随身设备 把值设为 -1
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }
        // 第四步 往数据库里面插入数据
        try {
            // 插入成功
            deviceMapper.insertSelective(device);
        } catch (Exception e) {
            // 插入设备数据到设备数据表里面 删除数据
            //删除iot中的设备数据 请求的参数 调用IOT接口  首先的第一步就是先封装请求的参数
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setDeviceName(device.getDeviceName());
            deleteDeviceRequest.setIotId(device.getIotId());
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            deleteDeviceRequest.setProductKey(device.getProductKey());
            try {
                client.deleteDevice(deleteDeviceRequest);
            } catch (Exception ex) {
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);
        }
        return ResponseResult.success();
    }

    /**
     * 分页查询设备的列表信息
     *
     * @param devicePageQueryDto
     * @return
     */
    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        // 开启分页查询
        PageHelper.startPage(devicePageQueryDto.getPageNum(), devicePageQueryDto.getPageSize());
         Page<DeviceVo> page = deviceMapper.pageQueryDevice(devicePageQueryDto);
         if(page==null || page.getTotal()<=0){
             throw new RuntimeException("没有数据");
         }
        return PageResponse.of(page , DeviceVo.class);
    }

    /**
     * 查询设备的详细的数据信息
     *
     * @param request
     * @return
     */
    @Override
    public DeviceVo queryDeviceDetail(QueryDeviceDetailRequest request) {
        // 构建请求的参数、请求IOT的阿里云平台
       /* QueryDeviceDetailRequest queryDeviceDetailRequest = new QueryDeviceDetailRequest();
        queryDeviceDetailRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        queryDeviceDetailRequest.setProductKey(deviceDto.getProductKey());
        queryDeviceDetailRequest.setIotId(deviceDto.getIotId());*/
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        // 调用IOT的API接口 查询设备的详细的数据信息
        QueryDeviceDetailResponse response = null;
        try {
             response = client.queryDeviceDetail(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        // 判断接口是否成功
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        // 拷贝属性
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = response.getBody().getData();
        // 根据设备的IotId 查询数据表
        DeviceVo deviceVo = deviceMapper.queryDeviceDetail(response.getBody().getData().getIotId());
        // 拷贝值 这里需要注意 data里面的非空的属性将拷贝到deviceVo对象里面 忽略里面的空的属性值 简化代码的书写
        BeanUtil.copyProperties(data ,deviceVo , CopyOptions.create().ignoreNullValue());
        return deviceVo;
    }

    /**
     * 查询指定物理模型的所有的属性运行的状态
     *
     * @param request
     * @return
     */
    @Override
    public ResponseResult queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDevicePropertyStatusResponse response = null;
        try {
             response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            // 失败就抛出异常
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        if(!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        // 返回的就是getData的数据
        return ResponseResult.success(response.getBody().getData());
    }

    /**
     * 查询已发布物理模型中的功能定义详情
     *
     * @param request
     * @return
     */
    @Override
    public ResponseResult queryThingModelPublished(QueryThingModelPublishedRequest request) {
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryThingModelPublishedResponse response = null;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
           throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        if(!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        return ResponseResult.success(response.getBody().getData());
    }

    /**
     * 修改设备的备注名称
     *
     * @param deviceDto
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        // 1. 先检查一下数据库里面是否有这个设备
        Device device = deviceMapper.selectByPrimaryKey(deviceDto.getId());
        if(ObjectUtil.isEmpty(device)){
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }
        // 调用物联网接口
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        // 需要什么传什么
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo nicknameInfo = BeanUtil.toBean(deviceDto, BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class);
        request.setDeviceNicknameInfo(Lists.newArrayList(nicknameInfo));
        // 调用已经定义好的Api的方法
        BatchUpdateDeviceNicknameResponse response;
        try {
            response = client.batchUpdateDeviceNickname(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        if (ObjectUtil.notEqual(Boolean.TRUE, response.getBody().getSuccess())) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        // 判断一下设备的值
        Device dev = BeanUtil.copyProperties(deviceDto, Device.class);
        if (dev.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())) {
            dev.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }
        // 调用mapper更新一下数据
        deviceMapper.updateByPrimaryKeySelective(dev);
    }

    /**
     * 删除设备
     *
     * @param deviceDto
     */
    @Override
    public void deleteDevice(DeviceDto deviceDto) {
        /**
         * 删除Iot里面的设备信息
         */
        DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
        deleteDeviceRequest.setIotId(deviceDto.getIotId());
        deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        try {
            client.deleteDevice(deleteDeviceRequest);
        } catch (Exception ex) {
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }
       // 删除device设备表里面的设备数据
        deviceMapper.deleteByIotId(deviceDto.getIotId());
    }


}
